﻿using System;
using System.Collections.Generic;
using mshtml;

namespace MQuery
{
    /// <summary>
    /// Selector's interface
    /// </summary>
    interface ISelector
    {
        /// <summary>
        /// Selects from a specified document.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <returns>List of selected elements</returns>
        IList<IHTMLElement> Select(HTMLDocumentClass document);
        /// <summary>
        /// Selects from a specified element.
        /// </summary>
        /// <param name="element">The element to select under.</param>
        /// <returns>List of selected elements</returns>
        IList<IHTMLElement> Select(IHTMLElement element);
        /// <summary>
        /// Selects from a specified elements list.
        /// </summary>
        /// <param name="from">List of elements to select under.</param>
        /// <returns>List of selected elements</returns>
        IList<IHTMLElement> Select(IList<IHTMLElement> from);
    }

    /// <summary>
    /// Base class for selectors
    /// </summary>
    abstract class Selector : ISelector
    {
        protected string selectString;

        protected Selector( string selectString )
        {
            this.selectString = selectString;
        }

        #region ISelector Members

        public IList<IHTMLElement> Select(HTMLDocumentClass document)
        {
            IList<IHTMLElement> to = new List<IHTMLElement>();
            SelectInto(document, to);
            return to;
        }

        public IList<IHTMLElement> Select(IHTMLElement element)
        {
            IList<IHTMLElement> to = new List<IHTMLElement>();
            SelectInto(element, to);
            return to;
        }

        public IList<IHTMLElement> Select(IList<IHTMLElement> from)
        {
            IList<IHTMLElement> to = new List<IHTMLElement>();
            foreach (var fromElement in from)
            {
                SelectInto(fromElement, to);
            }
            return to;
        }

        #endregion

        protected abstract void SelectInto(IHTMLElement from, IList<IHTMLElement> to);
        protected abstract void SelectInto(HTMLDocumentClass from, IList<IHTMLElement> to);
    }

    /// <summary>
    /// $(http://docs.jquery.com/Selectors/id)
    /// </summary>
    class IDSelector : Selector
    {
        public IDSelector(string id): base( id ){}

        protected override void SelectInto(IHTMLElement from, IList<IHTMLElement> to)
        {
            throw new NotImplementedException();
        }

        protected override void SelectInto(HTMLDocumentClass from, IList<IHTMLElement> to)
        {
            IHTMLElement element = from.getElementById(selectString);
            if (element != null)
            {
                to.Add(element);
            }
        }
    }

    /// <summary>
    /// $(http://docs.jquery.com/Selectors/element)
    /// </summary>
    class ElementSelector : Selector
    {
        public ElementSelector(string tag): base( tag ){}

        protected override void SelectInto(IHTMLElement from, IList<IHTMLElement> to)
        {
            foreach (IHTMLElement element in ((IHTMLElement2)from).getElementsByTagName(selectString))
            {
                to.Add(element);
            }
        }

        protected override void SelectInto(HTMLDocumentClass from, IList<IHTMLElement> to)
        {
            foreach (IHTMLElement element in from.getElementsByTagName(selectString))
            {
                to.Add(element);
            }
        }
    }

    /// <summary>
    /// $(http://docs.jquery.com/Selectors/class)
    /// </summary>
    class ClassSelector : Selector
    {
        public ClassSelector(string className): base( className ){}

        protected override void SelectInto(IHTMLElement from, IList<IHTMLElement> to)
        {
            from.Traverse(x =>
                {
                    if (x.HasClass(selectString))
                    {
                        to.Add(x);
                    }
                });            
        }

        protected override void SelectInto(HTMLDocumentClass from, IList<IHTMLElement> to)
        {
            SelectInto(from.documentElement, to);
        }
    }
}
